#ifndef ___fat32_funciones_utf

#define ___fat32_funciones_utf 1

#include <stdlib.h>
#include "f32_base.c"
#include "operbits.c"

unsigned short * fat32_convertir_utf8_a_utf16(const char *);
char * fat32_convertir_utf16_a_utf8(const unsigned short *);
unsigned int strlen16(const unsigned short *);
int strcmp16(const unsigned short *, const unsigned short *);
void strcat16(unsigned short *, const unsigned short *);

unsigned short * fat32_convertir_utf8_a_utf16 (const char * cadena) {
  unsigned int posicion;
  unsigned short * resultado = NULL;
  unsigned long int temp, temp2;
  unsigned int cantidad = 0;
  unsigned char temp1 = 0;
  unsigned short c1, c2;
  for (posicion = 0; cadena[posicion]; posicion ++)
    switch (temp1) {
    case 0:
      if (cadena[posicion] < 128) {
        resultado = fb_cambiar_tamano(resultado, sizeof(unsigned short), ++ cantidad);
        resultado[cantidad - 1] = cadena[posicion];
      } else if ((cadena[posicion] < 191) || (cadena[posicion] >= 248)) {
        if (resultado) free(resultado);
        return NULL;
      } else {
        if (cadena[posicion] < 224) temp1 = 1;
        else if (cadena[posicion] < 240) temp1 = 2;
        else temp1 = 3;
        temp2 = ~((1 << 8) - (1 << (6 - temp1)));
        temp2 &= (unsigned long int) cadena[posicion];
        temp = temp2 << (6 * temp1);
      }
      break;
    case 1:
      if ((cadena[posicion] < 128) || (cadena[posicion] > 191)) {
        if (resultado) free(resultado);
        return NULL;
      }
      temp2 = 63;
      temp2 &= (unsigned long int) cadena[posicion];
      temp += temp2;
      temp1 = 0;
      if ((temp < 128) || (temp > 1114111) || ((temp >= 55296) && (temp < 57344))) {
        if (resultado) free(resultado);
        return NULL;
      }
      if (temp > 65536) {
        temp -= 65536;
        temp2 = temp;
        temp2 >>= 10;
        temp &= 1023;
        c1 = 55296 + temp2;
        c2 = 56320 + temp;
        cantidad += 2;
        resultado = fb_cambiar_tamano(resultado, sizeof(unsigned short), cantidad);
        resultado[cantidad - 2] = c1;
        resultado[cantidad - 1] = c2;
      } else {
        c1 = temp;
        resultado = fb_cambiar_tamano(resultado, sizeof(unsigned short), ++ cantidad);
        resultado[cantidad - 1] = c1;
      }
      break;
    case 2: case 3:
      if ((cadena[posicion] < 128) || (cadena[posicion] > 191)) {
        if (resultado) free(resultado);
        return NULL;
      }
      temp2 = 63;
      temp2 &= (unsigned long int) cadena[posicion];
      temp1 --;
      temp += temp2 << (6 * temp1);
    }
  resultado = fb_cambiar_tamano(resultado, sizeof(unsigned short), cantidad + 1);
  resultado[cantidad] = 0;
  return resultado;
}

char * fat32_convertir_utf16_a_utf8 (const unsigned short * cadena) {
  char * resultado = NULL;
  unsigned int cantidad = 0;
  unsigned int posicion;
  unsigned long int temp, temp2;
  unsigned char temp1 = 0;
  unsigned char mascara, valor, tam;
  for (posicion = 0; cadena[posicion]; posicion ++)
    switch (temp1) {
    case 0:
      if (cadena[posicion] > 65535) {
        if (resultado) free(resultado);
        return NULL;
      }
      if ((cadena[posicion] < 55296) || (cadena[posicion] >= 57344)) {
        temp = cadena[posicion];
        if (temp < 128) {
          resultado = fb_cambiar_tamano(resultado, 1, ++ cantidad);
          resultado[cantidad - 1] = temp;
        } else {
          if (temp < 2048) tam = 2; else tam = 3;
          temp2 = (1 << 8) - (1 << (8 - tam));
          mascara = temp2;
          while (tam) {
            tam --;
            valor = obtenerBits(temp, 6 * tam, 6);
            valor += mascara;
            resultado = fb_cambiar_tamano(resultado, 1, ++ cantidad);
            resultado[cantidad - 1] = valor;
            mascara = 128;
          }
        }
      } else if (cadena[posicion] < 56320) {
        temp1 = 1;
        temp2 = cadena[posicion];
        temp2 -= 55296;
        temp = temp2 << 10;
      } else {
        if (resultado) free(resultado);
        return NULL;
      }
      break;
    case 1:
      if ((cadena[posicion] < 56320) || (cadena[posicion] >= 57344)) {
        if (resultado) free(resultado);
        return NULL;
      }
      temp2 = cadena[posicion];
      temp2 -= 56320;
      temp += temp2;
      temp += 65536;
      if (temp > 1114111) {
        if (resultado) free(resultado);
        return NULL;
      }
      tam = 4;
      mascara = 240;
      while (tam) {
        tam --;
        valor = obtenerBits(temp, 6 * tam, 6);
        valor += mascara;
        resultado = fb_cambiar_tamano(resultado, 1, ++ cantidad);
        resultado[cantidad - 1] = valor;
        mascara = 128;
      }
    }
  resultado = fb_cambiar_tamano(resultado, 1, cantidad + 1);
  resultado[cantidad] = 0;
  return resultado;
}

unsigned int strlen16 (const unsigned short * cadena) {
  const unsigned short * pos;
  for (pos = cadena; *pos; pos ++);
  return pos - cadena;
}

int strcmp16 (const unsigned short * cadena1, const unsigned short * cadena2) {
  const unsigned short * p1;
  const unsigned short * p2;
  for (p1 = cadena1, p2 = cadena2; *p1 && *p2; p1 ++, p2 ++)
    if (*p1 != *p2) {
      if (*p1 > *p2)
        return -1;
      else
        return 1;
    }
  if (*p1) return -1;
  if (*p2) return 1;
  return 0;
}

void strcat16 (unsigned short * destino, const unsigned short * origen) {
  unsigned short * p1;
  const unsigned short * p2;
  for (p1 = destino; *p1; p1 ++);
  for (p2 = origen; *p2; p1 ++, p2 ++)
    *p1 = *p2;
  *p1 = 0;
}

#endif